#pragma region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU Lesser General Public License.
/// </copyright>
#pragma endregion

#pragma once

template <class T, const IID* piid, class CDV = CComDynamicUnkArray >
class ATL_NO_VTABLE IConnectionPointGITImpl : public _ICPLocator<piid>
{
	typedef CComEnum<IEnumConnections, &__uuidof(IEnumConnections), CONNECTDATA,
		_Copy<CONNECTDATA> > CComEnumConnections;
	typedef CDV _CDV;

public:
	~IConnectionPointGITImpl();
	STDMETHOD(_LocCPQueryInterface)(REFIID riid, void ** ppvObject)
	{
#ifndef _ATL_OLEDB_CONFORMANCE_TESTS
		ATLASSERT(ppvObject != NULL);
#endif
		if (ppvObject == NULL)
			return E_POINTER;
		*ppvObject = NULL;

		if (InlineIsEqualGUID(riid, __uuidof(IConnectionPoint)) || InlineIsEqualUnknown(riid))
		{
			*ppvObject = this;
			AddRef();
#ifdef _ATL_DEBUG_INTERFACES
			_AtlDebugInterfacesModule.AddThunk((IUnknown**)ppvObject, _T("IConnectionPointGITImpl"), riid);
#endif // _ATL_DEBUG_INTERFACES
			return S_OK;
		}
		else
			return E_NOINTERFACE;
	}

	STDMETHOD(GetConnectionInterface)(IID* piid2)
	{
		if (piid2 == NULL)
			return E_POINTER;
		*piid2 = *piid;
		return S_OK;
	}

	STDMETHOD(GetConnectionPointContainer)(IConnectionPointContainer** ppCPC)
	{
		T* pT = static_cast<T*>(this);
		// No need to check ppCPC for NULL since QI will do that for us
		return pT->QueryInterface(__uuidof(IConnectionPointContainer), (void**)ppCPC);
	}

	HRESULT GetInterfaceFromGlobal(DWORD cpCookie, IUnknown** pUnk)
	{
		if(pUnk == NULL) return E_POINTER;

		T* pT = static_cast<T*>(this);
		pT->Lock();
		IUnknown* p = m_vec.GetUnknown(cpCookie);
		pT->Unlock();

		if(p == NULL) return CONNECT_E_NOCONNECTION;

		DWORD gitCookie = (DWORD)(UINT_PTR)p;
		CComPtr<IGlobalInterfaceTable> git;
		HRESULT hr = git.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
		if(FAILED(hr)) return hr;

		IID iid;
		GetConnectionInterface(&iid);
		hr = git->GetInterfaceFromGlobal(gitCookie, iid, (void**)pUnk);

		return hr;
	}

	HRESULT GetInterfaceFromGlobal(int index, IUnknown** pUnk)
	{
		if(pUnk == NULL) return E_POINTER;

		T* pT = static_cast<T*>(this);
		pT->Lock();
		IUnknown* p = m_vec.GetAt(index);
		pT->Unlock();

		if(p == NULL) return CONNECT_E_NOCONNECTION;

		DWORD gitCookie = (DWORD)(UINT_PTR)p;
		CComPtr<IGlobalInterfaceTable> git;
		HRESULT hr = git.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
		if(FAILED(hr)) return hr;

		IID iid;
		GetConnectionInterface(&iid);
		hr = git->GetInterfaceFromGlobal(gitCookie, iid, (void**)pUnk);

		ATLTRACE_TS(atlTraceConnectionPointGITImpl, TraceLevel::Verbose,
			"IConnectionPointGITImpl::GetInterfaceFromGlobal(0x%08X, OUT 0x%08X) : 0x%08X\r\n", gitCookie, pUnk, hr);
		return hr;
	}

	STDMETHOD(Advise)(IUnknown* pUnkSink, DWORD* pdwCookie);
	STDMETHOD(Unadvise)(DWORD dwCookie);
	STDMETHOD(EnumConnections)(IEnumConnections** ppEnum);
	CDV m_vec;
};

template <class T, const IID* piid, class CDV>
IConnectionPointGITImpl<T, piid, CDV>::~IConnectionPointGITImpl()
{
	CComPtr<IGlobalInterfaceTable> git;
	HRESULT hr = git.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
	if(FAILED(hr)) return;

	IUnknown** pp = m_vec.begin();
	while (pp < m_vec.end())
	{
		if(*pp != NULL)
		{
			DWORD gitCookie = (DWORD)(UINT_PTR)(*pp);
			git->RevokeInterfaceFromGlobal(gitCookie);
		}
		pp++;
	}
}

template <class T, const IID* piid, class CDV>
STDMETHODIMP IConnectionPointGITImpl<T, piid, CDV>::Advise(IUnknown* pUnkSink, DWORD* pdwCookie)
{
	T* pT = static_cast<T*>(this);
	IUnknown* p;
	HRESULT hRes = S_OK;
	if (pdwCookie != NULL)
		*pdwCookie = 0;
	if (pUnkSink == NULL || pdwCookie == NULL)
		return E_POINTER;
	IID iid;
	GetConnectionInterface(&iid);
	hRes = pUnkSink->QueryInterface(iid, (void**)&p);
	DWORD gitCookie = 0;
	if (SUCCEEDED(hRes))
	{
		CComPtr<IGlobalInterfaceTable> git;
		hRes = git.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
		if(SUCCEEDED(hRes))
		{
			hRes = git->RegisterInterfaceInGlobal(p, iid, &gitCookie);
			if(SUCCEEDED(hRes))
			{
				pT->Lock();
				*pdwCookie = m_vec.Add((IUnknown*)(UINT_PTR)gitCookie);
				hRes = (*pdwCookie != NULL) ? S_OK : CONNECT_E_ADVISELIMIT;
				pT->Unlock();
			}
		}
		p->Release();
	}
	else if (hRes == E_NOINTERFACE)
		hRes = CONNECT_E_CANNOTCONNECT;
	if (FAILED(hRes))
		*pdwCookie = 0;

	ATLTRACE_TS(atlTraceConnectionPointGITImpl, TraceLevel::Verbose,
		"IConnectionPointGITImpl::Advise(0x%08X-0x%08X, OUT 0x%08X) : 0x%08X\r\n", pUnkSink, p, gitCookie, hRes);

	return hRes;
}

template <class T, const IID* piid, class CDV>
STDMETHODIMP IConnectionPointGITImpl<T, piid, CDV>::Unadvise(DWORD dwCookie)
{
	ATLTRACE_TS(atlTraceConnectionPointGITImpl, TraceLevel::Verbose,
		"IConnectionPointGITImpl::Unadvise(0x%08X)\r\n", dwCookie);

	T* pT = static_cast<T*>(this);
	pT->Lock();
	IUnknown* p = m_vec.GetUnknown(dwCookie);
	HRESULT hRes = m_vec.Remove(dwCookie) ? S_OK : CONNECT_E_NOCONNECTION;
	pT->Unlock();

	if(p != NULL)
	{
		DWORD gitCookie = (DWORD)(UINT_PTR)p;
		CComPtr<IGlobalInterfaceTable> git;
		HRESULT hRes = git.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
		if(SUCCEEDED(hRes))
			git->RevokeInterfaceFromGlobal(gitCookie);
	}
	return hRes;
}

template <class T, const IID* piid, class CDV>
STDMETHODIMP IConnectionPointGITImpl<T, piid, CDV>::EnumConnections(IEnumConnections** ppEnum)
{
	if (ppEnum == NULL)
		return E_POINTER;
	*ppEnum = NULL;
	CComObject<CComEnumConnections>* pEnum = NULL;
	ATLTRY(pEnum = new CComObject<CComEnumConnections>)
	if (pEnum == NULL)
		return E_OUTOFMEMORY;
	T* pT = static_cast<T*>(this);
	pT->Lock();
	CONNECTDATA* pcd = NULL;
	ATLTRY(pcd = new CONNECTDATA[m_vec.end()-m_vec.begin()])
	if (pcd == NULL)
	{
		delete pEnum;
		pT->Unlock();
		return E_OUTOFMEMORY;
	}
	CONNECTDATA* pend = pcd;
	// Copy the valid CONNECTDATA's
	for(IUnknown** pp = m_vec.begin(); pp<m_vec.end(); pp++)
	{
		if(*pp != NULL)
		{
			pend->dwCookie = m_vec.GetCookie(pp);
			pend->pUnk = NULL;
			GetInterfaceFromGlobal(pend->dwCookie, &pend->pUnk);
			pend++;
		}
	}
	// don't copy the data, but transfer ownership to it
	pEnum->Init(pcd, pend, NULL, AtlFlagTakeOwnership);
	pT->Unlock();
	HRESULT hRes = pEnum->_InternalQueryInterface(__uuidof(IEnumConnections), (void**)ppEnum);
	if (FAILED(hRes))
		delete pEnum;
	return hRes;
}

